/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package com.mahumLogs.beans;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.Session;
import com.librairies.exceptions.CommandFailException;
import com.mahumLogs.backedbeans.Configuration;
import com.mahumLogs.backedbeans.Log;
import com.mahumLogs.backedbeans.LogError;
import com.mahumLogs.backedbeans.LogID;
import com.mahumLogs.backedbeans.LogsCalendarDate;
import com.mahumLogs.backedbeans.TopSite;
import com.mahumLogs.utils.AnalyseProcess;
import com.mahumLogs.utils.LogParser;
import com.mahumLogs.utils.LogsFile;
import com.mahumLogs.utils.LogsLector;
import com.mahumLogs.utils.MyReader;
import com.mahumLogs.utils.SSH;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import org.primefaces.context.RequestContext;
/**
 *
 * @author Kalast
 */

/**
 *
 * @author Kalast
 */
@Named
@ApplicationScoped
public class ScanLogsBean implements Serializable{

    @PersistenceContext
    private EntityManager em;
    @Resource
    private UserTransaction tx;
    
    private String currentDate;
    private String logsDirectoryPath;
    
    private String squidAdress;
    private String squidUser;
    private String squidPwd;
    private boolean scanEnable;
    
    private int progressScan;
    
    @Inject
    private ConfigurationBean confBean;
    
    private @Inject ClientSSHBean clientSSH;
    
    private @Inject LogsLector logsLector;
    
    private boolean analyseRunning;
    
    private ArrayList<LogsFile> fileToScan;
    private ArrayList<LogsFile> filesToAnalyse;
    private LogsFile fileToAnalyse;
    
     private int status;
    private String infosMessage;

    private static final int STATUS_INDISPO = -2;
    private static final int STATUS_INWAIT = -1;
    private static final int STATUS_SCAN_READY = 0;
    private static final int STATUS_ANALYSE_READY = 2;
    private static final int STATUS_ANAYSE_RUNNING = 3;

    public ScanLogsBean() {
        scanEnable = false;
        this.progressScan = 0;
        currentDate = new Date().toString();
        
        fileToScan = new ArrayList();
        filesToAnalyse = new ArrayList();
        analyseRunning = false;
        squidAdress = "?";
        squidUser = "?";
        logsDirectoryPath = "?";
        infosMessage = "En attente de réponse du serveur";
        status = STATUS_INWAIT;
    }

    public void addFileToAnalyse(LogsFile file){
        this.filesToAnalyse.add(file);
    }
    
    public void removeFile(LogsFile file){
        LogsFile fi = null;
        for(LogsFile f : this.filesToAnalyse){
            if(file.getFilename().equals(f.getFilename())){
                fi = f;
            }
        }
        if(fi != null){
            this.filesToAnalyse.remove(fi);
            RequestContext.getCurrentInstance().update("analyseFilesForm:listLogsFiles");
            RequestContext.getCurrentInstance().update("analyseFilesForm:buttonAnalyse");
        }
    }
    
    public static int getSTATUS_INDISPO() {
        return STATUS_INDISPO;
    }

    public static int getSTATUS_INWAIT() {
        return STATUS_INWAIT;
    }

    public static int getSTATUS_SCAN_READY() {
        return STATUS_SCAN_READY;
    }

    public static int getSTATUS_ANALYSE_READY() {
        return STATUS_ANALYSE_READY;
    }

    public static int getSTATUS_ANAYSE_RUNNING() {
        return STATUS_ANAYSE_RUNNING;
    }

    public boolean isAnalyseRunning() {
        return analyseRunning;
    }

    public int getStatus() {
        return status;
    }

    public ArrayList<LogsFile> getFilesToAnalyse() {
        return filesToAnalyse;
    }

    public void setFilesToAnalyse(ArrayList<LogsFile> filesToAnalyse) {
        this.filesToAnalyse = filesToAnalyse;
    }

    public void setStatus(int status) {
        this.status = status;
    }
    
    public String getLogsServerIp(){
        return confBean.findByKey("SQUID_SERVER_IP").getValeur();
    }
    
    public String getLogsServerPath(){
        return confBean.findByKey("SQUID_PATH_LOGS").getValeur();
    }
    
    public String getLogsServerUser(){
        return confBean.findByKey("SQUID_SERVER_USER").getValeur();
    }
    
    public void majScanStatus() {
        Configuration pathLogs = confBean.findByKey("SQUID_PATH_LOGS");
        logsDirectoryPath = pathLogs.getValeur();

        if (SSH.OS_SSH == SSH.OS_LINUX) {
            if (!logsDirectoryPath.endsWith("/")) {
                logsDirectoryPath += "/";
            }
        } else {
            if (!logsDirectoryPath.endsWith("\\")) {
                logsDirectoryPath += "\\";
            }
        }

        try {
            Channel c = this.clientSSH.execOnLogs("find " + logsDirectoryPath);
            MyReader my_reader = new MyReader(new InputStreamReader(c.getInputStream()));
            String line = my_reader.readLine();
            infosMessage = "Le serveur est disponible mais le dossier de logs est introuvable, vérifiez votre configuration.";
            if (line != null) {
                System.out.println(line);
                if (line.startsWith(logsDirectoryPath)) {
                    c.disconnect();
                    infosMessage = "Scan prêt";
                    status = STATUS_SCAN_READY;
                }
            }

        } catch (Exception ex) {
            infosMessage = "Serveur indisponible, vérifiez votre configuration.";
            status = STATUS_INDISPO;
        }
    }

    public String getInfosMessage() {
        return infosMessage;
    }

    public void setInfosMessage(String infosMessage) {
        this.infosMessage = infosMessage;
    }
    
    public void scan(){
        SSH.OS_SSH = SSH.OS_LINUX;
        System.out.println("DEBUT DU SCAN");
        progressScan += 10;
        fileToScan.clear();
        System.out.println("user : " + this.squidUser);
        Configuration pathLogs = confBean.findByKey("SQUID_PATH_LOGS");
        logsDirectoryPath = pathLogs.getValeur();
        
        if(SSH.OS_SSH == SSH.OS_LINUX){
            if (!logsDirectoryPath.endsWith("/")) {
                logsDirectoryPath += "/";
            }
        } else {
            if (!logsDirectoryPath.endsWith("\\")) {
                logsDirectoryPath += "\\";
            }
        }
        fileToScan.addAll(logsLector.detectLogsFile(logsDirectoryPath, "access.log"));
        
        System.out.println("FIN DU SCAN " + fileToScan.size());
    }

    public ClientSSHBean getClientSSH() {
        return clientSSH;
    }
    
    public void stopAnalyse(){
        this.analyseRunning = false;
    }
    
    public List<LogID> getLogsIDAtTime(long startTime, long endTime){
        return em.createQuery("SELECT l.atTime FROM Log l WHERE l.atTime.time BETWEEN " + startTime + " AND " + endTime + " ORDER BY l.atTime.id ASC").getResultList();
    }
    
    public void analyse(){
        RequestContext context = RequestContext.getCurrentInstance();
        if(analyseRunning){
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Analyse", "Une analyse est déjà en cours..."));
            context.update("growl");
        } else {
            analyseRunning = true;
            int inDB = 0;
            ArrayList<LogsFile> copyToAnalyse = new ArrayList();
            copyToAnalyse.addAll(this.filesToAnalyse);
            for (int i = 0; i < copyToAnalyse.size() && analyseRunning; i++) {
                this.fileToAnalyse = copyToAnalyse.get(i);
                inDB = checkLogsFileSizeDB(this.fileToAnalyse);
                System.err.println("In DB : " + inDB);
                System.err.println("In File : " + this.fileToAnalyse.getNbLignes());
                if (inDB == this.fileToAnalyse.getNbLignes()) {
                    this.fileToAnalyse.setNbLigneInsertDB(this.fileToAnalyse.getNbLignes());
                } else {
                    System.out.println("Start for " + this.fileToAnalyse.getFilename());
                    updateInfosMessage();
                    copyLogsFileToDB();
                    this.removeFile(fileToAnalyse);
                    context.update("analyseFilesForm:listLogsFiles");
                    context.update("analyseFilesForm:buttonAnalyse");
                }
            }
            //PeriodicScanEJB.startScan(scan.getSquidAdress(), scan.getSquidUser(), scan.getSquidPwd(), scan.getLogsDirectoryPath() + "access.log", 0);
            System.err.println("L'analyse est terminée");
            this.analyseRunning = false;
        }
    }
    
    public int checkLogsFileSizeDB(LogsFile file){
        Calendar c = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        
        c.setTime(file.getStartTimestamp());
        c2.setTime(file.getEndTimestamp());
        
        
        System.out.println("Search pour le jour : ");
        System.out.println(c.get(Calendar.DATE) + "/" + c.get(Calendar.MONTH) + "/" + c.get(Calendar.YEAR));
        
        System.out.println("Start requete");
        long time = System.currentTimeMillis();
        
        try{
            Object size = em.createQuery("SELECT COUNT(l.atTime.time) FROM Log l WHERE "
                + "l.atTime.time BETWEEN :ddate AND :fdate")
                .setParameter("ddate", c.getTimeInMillis())
                .setParameter("fdate", c2.getTimeInMillis())
            .getSingleResult();
            
            System.out.println("Nombre de logs : " + size);
            System.out.println("Temps de la requete : " + (((double)System.currentTimeMillis() - (double)time)/1000f) + " secs");
            return Integer.valueOf(size.toString());
        } catch(Exception ex){
            System.err.println("Error Mysql COnnection (checkLogsFileSizeDB Method)");
        }
        
        return 0;
    }
    
    public void copyLogsFileToDB(){
        System.err.println("Analyse du fichier " + this.fileToAnalyse.getFilename());
        List<LogID> logsID = this.getLogsIDAtTime(this.fileToAnalyse.getStartTimestamp().getTime(), this.fileToAnalyse.getEndTimestamp().getTime());
        System.out.println("On attend " + logsID.size() + " logs");
        
        int i = 1;
        try {
            /*System.out.println("Connexion au serveur SSH");
            System.out.println("cat " + this.fileToAnalyse.getPath());
            Channel chan = clientSSH.execOnLogs("cat " + this.fileToAnalyse.getPath());
            MyReader reader = new MyReader(new InputStreamReader(chan.getInputStream()));*/
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(this.fileToAnalyse.getPath() + "/" + this.fileToAnalyse.getFilename())));
            System.out.println("Ouyverture et lecture de la reposne");
            String lineLog = "";
            this.fileToAnalyse.resetNbLigneInsertDB();
            System.out.println("reset line");
            Log l = null;
            LogError le = null;
            TopSite ts = null;
            int count = 0;
            try {
                System.out.println("On démarre la transaction");
                tx.begin();
            } catch (NotSupportedException ex) {
                System.err.println(ex);
            } catch (SystemException ex) {
                System.err.println(ex);
            }
            System.out.println("On parcours les ligne de logs + " + this.analyseRunning);
            Calendar cal = Calendar.getInstance();
            while ((lineLog = reader.readLine()) != null && this.analyseRunning) {
                i++;
                if (i > logsID.size()) {
                    l = LogParser.parse(lineLog, i);
                    if (l != null) {
                        em.merge(l);
                        cal.setTimeInMillis(l.getAtTime());

                        try {
                            ts = (TopSite) em.createQuery("SELECT ts FROM TopSite ts"
                                    + " WHERE ts.domaine LIKE '" + l.getDomaine() + "'"
                                    + " AND ts.annee=:lcd_a"
                                    + " AND ts.mois=:lcd_m"
                                    + " AND ts.jour=:lcd_j"
                                    + " AND ts.heure=:lcd_h")
                                    .setParameter("lcd_a", cal.get(Calendar.YEAR))
                                    .setParameter("lcd_m", cal.get(Calendar.MONTH) + 1)
                                    .setParameter("lcd_j", cal.get(Calendar.DATE))
                                    .setParameter("lcd_h", cal.get(Calendar.HOUR_OF_DAY))
                                    .getSingleResult();
                            if (ts != null) {
                                ts.setVisites(ts.getVisites() + 1);
                                ts.setDataSize(ts.getDataSize() + l.getRequestSize());
                            }
                        } catch (Exception ex) {
                            ts = new TopSite();
                            ts.setDomaine(l.getDomaine());
                            ts.setAnnee(cal.get(Calendar.YEAR));
                            ts.setMois(cal.get(Calendar.MONTH) + 1);
                            ts.setJour(cal.get(Calendar.DATE));
                            ts.setHeure(cal.get(Calendar.HOUR_OF_DAY));
                            ts.setVisites(1);
                            ts.setDataSize(l.getRequestSize());
                        }
                        em.merge(ts);
                    } else {
                        le = new LogError(Long.valueOf(lineLog.replaceAll("\\s+", " ").split(" ")[0].replace(".", "")), lineLog.replaceAll("\\s+", " "));
                        em.merge(le);
                    }

                    
                    this.fileToAnalyse.incrementNbLigneInsertDB();
                    count++;

                    try {
                        if (count >= 1000) {
                            try {
                                tx.commit();
                                em.clear();
                            } catch (RollbackException ex) {
                                System.err.println(ex.getMessage());
                            } catch (HeuristicMixedException ex) {
                                System.err.println(ex.getMessage());
                            } catch (HeuristicRollbackException ex) {
                                System.err.println(ex.getMessage());
                            }

                            tx.begin();
                            count = 0;
                        }
                    } catch (NotSupportedException ex) {
                        System.err.println("Une erreur est survenue1");
                    } catch (SystemException ex) {
                        System.err.println("Une erreur est survenue2");
                    } catch (SecurityException ex) {
                        System.err.println("Une erreur est survenue3");
                    } catch (IllegalStateException ex) {
                        System.err.println("Une erreur est survenue4");
                    }
                } else {
                    
                }
            }
            try {
                tx.commit();
                em.clear();
            } catch (RollbackException ex) {
                System.err.println(ex);
            } catch (HeuristicMixedException ex) {
                System.err.println(ex);
            } catch (HeuristicRollbackException ex) {
                System.err.println(ex);
            } catch (SecurityException ex) {
                System.err.println(ex);
            } catch (IllegalStateException ex) {
                System.err.println(ex);
            } catch (SystemException ex) {
                System.err.println(ex);
            }
        } catch (IOException ex) {
            System.err.println(ex);
        }
        
        System.err.println("Analyse du fichier " + this.fileToAnalyse.getFilename());
        displayMessage("Analyse", "Analyse du fichier " + this.fileToAnalyse.getFilename() + " terminée");
    }
    
    public void displayMessage(String title, String content, Severity...severity){
        try{
           FacesMessage msg = new FacesMessage(title, content);
            if(severity.length > 0){
                msg.setSeverity(severity[0]);
            }
            FacesContext.getCurrentInstance().addMessage(null, msg);
            RequestContext.getCurrentInstance().update("growl"); 
        } catch(Exception e){
            System.err.println("FacesContext indisponible");
        }
    }
    
    public void updateInfosMessage(){
        try{
            RequestContext.getCurrentInstance().update(":infos_message");
        } catch(Exception e){
            System.err.println("RequestContext indisponible");
        }
    }

    public void setFileToAnalyse(LogsFile fileToAnalyse) {
        this.fileToAnalyse = fileToAnalyse;
    }

    public void setAnalyseRunning(boolean analyseRunning) {
        this.analyseRunning = analyseRunning;
    }

    public String getSquidPwd() {
        return squidPwd;
    }
    
    public void testDate(){
        System.out.println("Start requete");
        long time = System.currentTimeMillis();
        
        List<Long> list = em.createQuery("SELECT l.atTime FROM Log l GROUP BY FUNC('FLOOR',l.atTime / 86400000)")
            .getResultList();
        
        System.out.println("Nombre de logs : " + list.size());
        for(Long l : list){
            System.out.println(new Date(l));
        }
        System.out.println("Temps de la requete : " + (((double)System.currentTimeMillis() - (double)time)/1000f) + " secs");
    }

    public LogsFile getFileToAnalyse() {
        return fileToAnalyse;
    }

    public ArrayList<LogsFile> getFileToScan() {
        return fileToScan;
    }

    public String getLogsDirectoryPath() {
        return logsDirectoryPath;
    }

    public String getSquidAdress() {
        return squidAdress;
    }

    public UserTransaction getTransaction(){
        return tx;
    }
    
    public EntityManager getEntityManager(){
        return em;
    }
    
    public String getSquidUser() {
        return squidUser;
    }

    public String getCurrentDate() {
        Date timestamp = lastTimestampInDB();
        return timestamp != null ? timestamp.toString() : "Aucun log archivé";
    }

    public int getProgressScan() {
        return progressScan;
    }

    public void setProgressScan(int progressScan) {
        this.progressScan = progressScan;
    }
    
    public Date lastTimestampInDB(){
        try{
            Query query = em.createQuery("SELECT l FROM Log l" 
                + " ORDER BY l.atTime desc");
            query.setMaxResults(1);
            Log l = (Log)query.getSingleResult();
            return new Date(l.getAtTime());
        } catch(Exception ex){
            System.err.println("Aucun logs n'est encore archivé !");
        }
        return null;
    }
}
